package com.yun.zf.system.security.provider;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.yun.zf.common.admin.domain.LoginUser;
import com.yun.zf.common.app.domain.SysUserAuth;
import com.yun.zf.common.app.dto.wechat.WxLoginResultDto;
import com.yun.zf.system.security.token.WxAppletAuthenticationToken;
import com.yun.zf.system.service.ISysMenuService;
import com.yun.zf.system.service.ISysUserService;
import com.yun.zf.system.service.SysUserAuthService;
import com.yun.zf.system.utils.WechatDecryptDataUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


@Slf4j
@Component
public class WxAppletAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private SysUserAuthService userAuthService;
    @Autowired
    private ISysMenuService menuService;
    @Autowired
    private ISysUserService userService;

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;
    @Value("${wx.appid}")
    private String appid;
    @Value("${wx.secret}")
    private String appsrcret;
    private String wxAuthUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";


    // todo,微信登录和手机号码获取好像存在冲突
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        WxAppletAuthenticationToken wxToken = null;
        if (authentication instanceof WxAppletAuthenticationToken) {
            wxToken = (WxAppletAuthenticationToken) authentication;
        }
        // 获取微信登录信息
       Map<String,Object>map  = wxLogin(wxToken);
        // 执行完成后跳转到 handle 下面的成功处理方法
        return new WxAppletAuthenticationToken((LoginUser) map.get("user"),map.get("sessionKey").toString(), null);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (WxAppletAuthenticationToken.class
                .isAssignableFrom(authentication));
    }

    public Map<String,Object> wxLogin(WxAppletAuthenticationToken wxToken) {
        // 验证微信登录
        String url = String.format(wxAuthUrl, appid, appsrcret, wxToken.getUserInfoMo().getCode());
        log.debug("wx auth url: [{}]", url);
        //调用微信api 进行授权登录
        String result = HttpUtil.get(url);
        WxLoginResultDto resultDto = JSONObject.parseObject(result, WxLoginResultDto.class);
        //todo 模拟数据
        String comSignature = "";
        //签名校验信息是否一致
        if (resultDto != null && resultDto.getErrcode() != 0) {
            log.error("wx auth failed, errCode is [{}], errMsg is [{}]", resultDto.getErrcode(), resultDto.getErrmsg());
            throw new BadCredentialsException("微信认证失败");
        }
        /*try {
            comSignature = getsignature2(wxToken.getUserInfoMo().getRawData(), resultDto.getSession_key());
        } catch (Exception e) {
            throw new BadCredentialsException("签名验证失败");
        }
        if (!wxToken.getUserInfoMo().getSignature().equals(comSignature)) {
            log.error("signature is invalid, [{}] vs [{}]", comSignature, wxToken.getUserInfoMo().getSignature());
            throw new BadCredentialsException("校验失败");
        }*/
        // 验证完成之后解析手机号码
        WechatDecryptDataUtil dataUtil = new WechatDecryptDataUtil();

        LoginUser user = new LoginUser();
        // 验证完成之后 查看是否登录过，如果为首次登录，则新增数据
        SysUserAuth userAuth = userAuthService.getUserByOpenId(resultDto.getOpenid(), "wx");
        try {
            if (userAuth == null) {
                userAuth = new SysUserAuth();
                userAuth.setOpenId(resultDto.getOpenid());
                userAuth.setSessionKey(resultDto.getSession_key());
                userAuth.setUnionid(resultDto.getUnionid());
                userAuth.setStatus(1);
                userAuth.setLoginType("wx");
                userAuth.setCreateTime(new Date());
            Long userId =  userService.addLoginUser(userAuth,wxToken.getUserInfoMo().getInviteId());
              user.setUserId(userId);
            } else {
                // 如果不为空，则进行数据更新
                userAuth.setSessionKey(resultDto.getSession_key());
                userAuthService.updateAuthByOpenId(userAuth);
                user.setUserId(userAuth.getUserId());
            }
        } catch (Exception e) {
            throw new BadCredentialsException("登录异常");
        }

        user.setOpendId(resultDto.getOpenid());
        user.setLoginType("wx");
        Map<String,Object> map = new HashMap<>();
        map.put("user",user);
        map.put("sessionKey",resultDto.getSession_key());
        return map;
    }


    // 新用户注册注册数据及绑定逻辑
    public Long newUser() {
        return 1L;
    }

    public static String getsignature2(String rawData, String sessionKey) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        String stringASCII = rawData + sessionKey;
        String cipherStr = null;
        try {
            //指定sha1算法
            MessageDigest digest = MessageDigest.getInstance("SHA");
            // 调用digest方法，进行加密操作
            byte[] cipherBytes = digest.digest(stringASCII.getBytes());
            cipherStr = Hex.encodeHexString(cipherBytes);
        } catch (NoSuchAlgorithmException e) {
            throw e;
        }
        return cipherStr;
    }
}
